﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.Text;
using System.Threading.Tasks;
using StyleMVVM.DependencyInjection;
#if NETFX_CORE
using Windows.UI.Xaml.Controls;

#else
using System.Windows.Controls;

#endif

namespace StyleMVVM.Suspension
{
	/// <summary>
	/// SuspensionManager captures global session state to simplify process lifetime management
	/// for an application.  Note that session state will be automatically cleared under a variety
	/// of conditions and should only be used to store information that would be convenient to
	/// carry across sessions, but that should be disacarded when an application crashes or is
	/// upgraded.
	/// </summary>
	public class SuspensionManager
	{
		private static readonly object lockObject = new object();
		private static readonly Dictionary<string, object> _sessionState = new Dictionary<string, object>();

		private const string sessionStateFilename = "_sessionState.xml";
		private static volatile IFrameSuspensionService frameSuspensionService;
		private static volatile ISyncService syncService;

		/// <summary>
		/// Provides access to global session state for the current session.  This state is
		/// serialized by <see cref="SaveAsync"/> and restored by
		/// <see cref="RestoreAsync"/>, so values must be serializable by
		/// <see cref="DataContractSerializer"/> and should be as compact as possible.  Strings
		/// and other self-contained data types are strongly recommended.
		/// </summary>
		public static Dictionary<string, object> SessionState
		{
			get { return _sessionState; }
		}

		/// <summary>
		/// Save the current <see cref="SessionState"/>.  Any <see cref="Frame"/> instances
		/// registered with <see cref="RegisterFrame"/> will also preserve their current
		/// navigation stack, which in turn gives their active <see cref="Page"/> an opportunity
		/// to save its state.
		/// </summary>
		/// <returns>An asynchronous task that reflects when session state has been saved.</returns>
		public static async Task SaveAsync()
		{
			await SyncService.Suspend();

			await FrameSuspensionService.SaveAsync();
		}

		/// <summary>
		/// Restores previously saved <see cref="SessionState"/>.  Any <see cref="Frame"/> instances
		/// registered with <see cref="RegisterFrame"/> will also restore their prior navigation
		/// state, which in turn gives their active <see cref="Page"/> an opportunity restore its
		/// state.
		/// </summary>
		/// <returns>An asynchronous task that reflects when session state has been read.  The
		/// content of <see cref="SessionState"/> should not be relied upon until this task
		/// completes.</returns>
		public static async Task RestoreAsync()
		{
			await SyncService.LoadStateFromDisk();

			await FrameSuspensionService.RestoreAsync();
		}

		/// <summary>
		/// Registers a <see cref="Frame"/> instance to allow its navigation history to be saved to
		/// and restored from <see cref="SessionState"/>.  Frames should be registered once
		/// immediately after creation if they will participate in session state management.  Upon
		/// registration if state has already been restored for the specified key
		/// the navigation history will immediately be restored.  Subsequent invocations of
		/// <see cref="RestoreAsync"/> will also restore navigation history.
		/// </summary>
		/// <param name="frame">An instance whose navigation history should be managed by
		/// <see cref="StyleMVVM.Suspension.SuspensionManager"/></param>
		/// <param name="sessionStateKey">A unique key into <see cref="SessionState"/> used to
		/// store navigation-related information.</param>
		public static void RegisterFrame(Frame frame, String sessionStateKey)
		{
			FrameSuspensionService.RegisterFrame(frame, sessionStateKey);
		}

		/// <summary>
		/// Disassociates a <see cref="Frame"/> previously registered by <see cref="RegisterFrame"/>
		/// from <see cref="SessionState"/>.  Any navigation state previously captured will be
		/// removed.
		/// </summary>
		/// <param name="frame">An instance whose navigation history should no longer be
		/// managed.</param>
		public static void UnregisterFrame(Frame frame)
		{
			FrameSuspensionService.UnregisterFrame(frame);
		}

		/// <summary>
		/// Provides storage for session state associated with the specified <see cref="Frame"/>.
		/// Frames that have been previously registered with <see cref="RegisterFrame"/> have
		/// their session state saved and restored automatically as a part of the global
		/// <see cref="SessionState"/>.  Frames that are not registered have transient state
		/// that can still be useful when restoring pages that have been discarded from the
		/// navigation cache.
		/// </summary>
		/// <remarks>Apps may choose to rely on <see cref="LayoutAwarePage"/> to manage
		/// page-specific state instead of working with frame session state directly.</remarks>
		/// <param name="frame">The instance for which session state is desired.</param>
		/// <returns>A collection of state subject to the same serialization mechanism as
		/// <see cref="SessionState"/>.</returns>
		public static IDictionary<String, Object> SessionStateForFrame(Frame frame)
		{
			return FrameSuspensionService.SessionStateForFrame(frame);
		}

		public static ISyncService SyncService
		{
			get
			{
				if (syncService == null)
				{
					lock (lockObject)
					{
						if (syncService == null)
						{
							syncService = Bootstrapper.Instance.Container.Locate<ISyncService>();
						}
					}
				}
				return syncService;
			}
		}

		private static IFrameSuspensionService FrameSuspensionService
		{
			get
			{
				if (frameSuspensionService == null)
				{
					lock (lockObject)
					{
						if (frameSuspensionService == null)
						{
							frameSuspensionService =
								Bootstrapper.Instance.Container.Locate<IFrameSuspensionService>();
						}
					}
				}

				return frameSuspensionService;
			}
		}
	}
}